
package net.zapasa.controller.catalog;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.zapasa.core.common.exception.WebException;
import net.zapasa.core.common.util.LogUtil;

import org.apache.commons.lang.StringUtils;
import org.broadleafcommerce.common.exception.ServiceException;
import org.broadleafcommerce.common.util.BLCSystemProperty;
import org.broadleafcommerce.core.catalog.domain.Category;
import org.broadleafcommerce.core.catalog.domain.Product;
import org.broadleafcommerce.core.catalog.service.CatalogService;
import org.broadleafcommerce.core.search.domain.ProductSearchCriteria;
import org.broadleafcommerce.core.search.domain.ProductSearchResult;
import org.broadleafcommerce.core.search.domain.SearchFacetDTO;
import org.broadleafcommerce.core.web.catalog.CategoryHandlerMapping;
import org.broadleafcommerce.core.web.controller.catalog.BroadleafCategoryController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * This class works in combination with the CategoryHandlerMapping which finds a
 * category based upon the passed in URL.
 */

@Controller("blCategoryController")
public class CategoryController extends BroadleafCategoryController {

    protected static String RANGE_ATTRIBUTE_NAME = "range";  
	
	@Resource(name = "blCatalogService")
	private CatalogService catalogService;

	@Override
	public ModelAndView handleRequest(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		LogUtil.info(this.getClass(), "Entra en blCategoryController");
		
		// TODO no entrar al handle, directamente mostrar todos los productos de la categoria actual..
		return super.handleRequest(request, response);
	}

	// TODO lo usamos si queremos actualizar parcialmente...
	@RequestMapping("/blCategoryController/updateProducts")
	public String updateProductsCategory(HttpServletRequest request, HttpServletResponse response, Model model) throws WebException {
		LogUtil.info(this.getClass(), "Entra en updateProductsCategory");
		String categoryId = null;
		try {
			
			categoryId = request.getParameter(CategoryHandlerMapping.CURRENT_CATEGORY_ATTRIBUTE_NAME);
			
			if(categoryId==null){
				throw new WebException("El parametro " + CategoryHandlerMapping.CURRENT_CATEGORY_ATTRIBUTE_NAME + " es obligatorio");
			}
			
			Category category = catalogService.findCategoryById(Long.valueOf(categoryId));
	
			List<SearchFacetDTO> availableFacets = getSearchService().getCategoryFacets(category);
			ProductSearchCriteria searchCriteria = buildSearchCriteria(request, availableFacets);
	
			String searchTerm = request.getParameter(ProductSearchCriteria.QUERY_STRING);
			ProductSearchResult result;
			if (StringUtils.isNotBlank(searchTerm)) {
				result = getSearchService().findProductsByCategoryAndQuery(category, searchTerm, searchCriteria);
			} else {
				result = getSearchService().findProductsByCategory(category,searchCriteria);
			}
	
			facetService.setActiveFacetResults(result.getFacets(), request);
	
			model.addAttribute(CATEGORY_ATTRIBUTE_NAME, category);
			model.addAttribute(PRODUCTS_ATTRIBUTE_NAME, result.getProducts());
			model.addAttribute(PRODUCTS_ATTRIBUTE_NAME, result.getProducts());
			model.addAttribute(FACETS_ATTRIBUTE_NAME, result.getFacets());
			model.addAttribute(PRODUCT_SEARCH_RESULT_ATTRIBUTE_NAME, result);
			if (result.getProducts() != null) {
				model.addAttribute(ALL_PRODUCTS_ATTRIBUTE_NAME, new HashSet<Product>(result.getProducts()));
			}
	
			// TODO esto que es??
//			addDeepLink(model, deepLinkService, category);
//	
//			ExtensionResultHolder<String> erh = new ExtensionResultHolder<String>();
//			templateOverrideManager.getProxy().getOverrideTemplate(erh, category);
//	
//			if (StringUtils.isNotBlank(erh.getResult())) {
//				model.setViewName(erh.getResult());
//			} else if (StringUtils.isNotEmpty(category.getDisplayTemplate())) {
//				model.setViewName(category.getDisplayTemplate());
//			} else {
//				model.setViewName(getDefaultCategoryView());
//			}
		
		} catch (ServiceException e) {
			LogUtil.error(this.getClass(), e, "Error al obtener los productos de la categoria="+categoryId);
			throw new WebException("No se pueden obtener los productos");
		}

		return defaultCategoryView + " :: productList";
	}

	// TODO crearnos un servicio heredando al suyo y creando un nuevo metodo que sera este..
	//facetService.buildSearchCriteria(request, availableFacets);
	protected ProductSearchCriteria buildSearchCriteria(HttpServletRequest request, List<SearchFacetDTO> availableFacets){
		// Construyo la consulta
		Map<String, String[]> parameters = new HashMap<String, String[]>(request.getParameterMap());
        Map<String, List<String>> activeFieldFilters = new HashMap<String, List<String>>();
        ArrayList<String> criterio = null;
        for (Iterator<Entry<String,String[]>> iter = parameters.entrySet().iterator(); iter.hasNext();){
            Map.Entry<String, String[]> entry = iter.next();
            String key = entry.getKey();
            
            // Si es un criterio de busqueda predefinido.. lo agrego a los filtros
            if (key.equals(ProductSearchCriteria.SORT_STRING) || 
            	key.equals(ProductSearchCriteria.PAGE_NUMBER) ||
            	key.equals(ProductSearchCriteria.PAGE_SIZE_STRING) ||
            	key.equals(ProductSearchCriteria.QUERY_STRING)) {
            	criterio = new ArrayList<String>();
            	for(String value : entry.getValue()){
            		criterio.add(value);
            	}
            	activeFieldFilters.put(key, criterio);
            } 
            // Sino, compruebo si es alguna de las caracteristicas (facet) activas..
            else {
	            for(SearchFacetDTO facet : availableFacets){
	            	String fieldName = facet.getFacet().getField().getAbbreviation();
		            if (key.startsWith(fieldName + "-")) {
		            	if(activeFieldFilters.get(fieldName)==null){
		            		activeFieldFilters.put(fieldName, new ArrayList<String>());
		            	}
		            	if(RANGE_ATTRIBUTE_NAME.equals(key.substring(key.indexOf('-') + 1))){
		            		activeFieldFilters.get(fieldName).add(entry.getValue()[0]);
		            	} else {
		            		activeFieldFilters.get(fieldName).add(key.substring(key.indexOf('-') + 1));
		            	}
		                
		                break;
		            }
		          
	            }
            }
            
            iter.remove();
        }
                
        for(String fieldName : activeFieldFilters.keySet()){
        	 parameters.put(fieldName, activeFieldFilters.get(fieldName).toArray(new String[activeFieldFilters.get(fieldName).size()]));
        }
 
		
		ProductSearchCriteria searchCriteria = new ProductSearchCriteria();
        searchCriteria.setPageSize(getDefaultPageSize());
        
        Map<String, String[]> facets = new HashMap<String, String[]>();
        
        for (Iterator<Entry<String,String[]>> iter = parameters.entrySet().iterator(); iter.hasNext();){
            Map.Entry<String, String[]> entry = iter.next();
            String key = entry.getKey();
            
            if (key.equals(ProductSearchCriteria.SORT_STRING)) {
                searchCriteria.setSortQuery(StringUtils.join(entry.getValue(), ","));
            } else if (key.equals(ProductSearchCriteria.PAGE_NUMBER)) {
                searchCriteria.setPage(Integer.parseInt(entry.getValue()[0]));
            } else if (key.equals(ProductSearchCriteria.PAGE_SIZE_STRING)) {
                int requestedPageSize = Integer.parseInt(entry.getValue()[0]);
                int maxPageSize = getMaxPageSize();
                searchCriteria.setPageSize(Math.min(requestedPageSize, maxPageSize));
            } else if (key.equals(ProductSearchCriteria.QUERY_STRING)) {
                continue; // This is handled by the controller
            } else if(entry!=null && entry.getValue().length>0){
                facets.put(key, entry.getValue());
            }
        }
        
        searchCriteria.setFilterCriteria(facets);
        
        return searchCriteria;

	}
	
    protected int getMaxPageSize() {
        return BLCSystemProperty.resolveIntSystemProperty("web.maxPageSize");
    }
	
    protected int getDefaultPageSize() {
        return BLCSystemProperty.resolveIntSystemProperty("web.defaultPageSize");
    }
	
}
